#region MIT Licence

//Copyright (c) 2007 Mono Overlay Routing Foundation Team
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.

#endregion


using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using MonORF.Networking.Factories;
using MonORF.Serialization;


namespace MonORF.Networking
{
   /// <summary>
   /// Description of BasicProtocolListener.
   /// </summary>
   public class UdpProtocolListener: BasicProtocolListener {

      //private Dictionary<IPEndPoint, Socket> socketListening;
      //private Socket socketListening;
      //private AsyncCallback m_receiveCompletion;
      // private AsyncCallback m_dataReceiveDispatch;
      //private Int32 m_messageSize = 0;
      //private Int64 m_bufferSize = 0;
      //private IPEndPoint m_listenEndPoint;
      //private Object m_lockObject = new Object();
      // private Boolean m_isActive = false;

       public UdpProtocolListener(AsyncCallback dataReceiveDispatch,
                                IPEndPoint listenEndPoint,
                                 Int32 messageSize, 
                                Int64 bufferSize):base(dataReceiveDispatch, listenEndPoint, 
                                                      messageSize, bufferSize)
      {
          m_dataReceiveDispatch = dataReceiveDispatch;
         m_listenEndPoint = listenEndPoint;
         m_messageSize = messageSize;
         m_bufferSize = bufferSize;
         m_receiveCompletion = new AsyncCallback(ReceiveCompleteAsyncCallBack);

         //create socket to listen using another thread, i used this to allow 
          // in future listen multiple sockets
         CreateSocket();
      }
      
      #region properties

       public Boolean IsActive{
           get { return m_isActive; }
           set{m_isActive = value;}

       }
      
      /// <summary>
      /// get local endpoint
      /// </summary>
      //      internal IPEndPoint LocalIPEndPoint{ get{ return m_localEndPoint;} }
      
      /// <summary>
      /// SyncLock object
      /// </summary>
      internal Object SyncLock{ get{return m_lockObject;} }
      
      #endregion
      
      
      #region helpers
      
      /// <summary>
      /// create socket and prepare them for start listen
      /// </summary>
      private override void CreateSocket()
      {
         base.socketListening = new Socket(AddressFamily.InterNetwork,
                                    SocketType.Dgram, ProtocolType.IPv4);

         base.socketListening.SetSocketOption(SocketOptionLevel.Socket,
                                    SocketOptionName.ReuseAddress, 1);

         base.socketListening.Bind(base.listenEndPoint);
         
         //star to listen
         WaitCallback continousListen = new WaitCallback(Listen);
         ThreadPool.QueueUserWorkItem(continousListen, base.socketListening);

      }
      
      #endregion

      /// <summary>
      /// assinchonous staring listener
      /// </summary>
      /// <param name="socketState"></param>
      public void Listen(Object socketState)
      {
         #region receive remote endpoint

         IPEndPoint client = new IPEndPoint(IPAddress.Any, 0);
         EndPoint remoteEP = (EndPoint)client;

         #endregion

         try
         {
            //            TransmissionState result = null;

            lock (SyncLock)
            {
               Byte[] tempBuffer = m_receiveBuffer.TakeBuffer(m_messageSize);
               
               IAsyncResult result =
                  ((Socket)socketState).BeginReceiveFrom(tempBuffer,0,tempBuffer.Length,
                                                         SocketFlags.None, ref remoteEP,
                                                         m_receiveCompletion,
                                                         new TransmissionState((Socket)socketState,
                                                                               tempBuffer));
            }
         }
         catch(SocketException ex)
         {
             if (ex.SocketErrorCode == SocketError.ConnectionReset)
                 return;
         }
      }
      
      /// <summary>
      /// completion estado for received data.
      /// </summary>
      /// <param name="result"></param>
      private void ReceiveCompleteAsyncCallBack(IAsyncResult result) {

         #region receive remote endpoint

         IPEndPoint client = new IPEndPoint(IPAddress.Any, 0);
         EndPoint remoteEP = (EndPoint)client;

         #endregion
         
            TransmissionState transmissionState = (TransmissionState)result.AsyncState;
          Byte[] receivedData = transmissionState.Buffer;

          try {
              lock (transmissionState.SyncLock)
              {
                  Int32 bytes = transmissionState.Socket.EndReceiveFrom(result, ref remoteEP);
                  if (bytes > 0)
                      receivedData = transmissionState.Buffer;

                  Listen(transmissionState.Socket);
              }

              //ThreadPool.QueueUserWorkItem(m_dataReceiveDispatch, receivedData);              
          }
         catch(Exception ex) {
             throw ex;
         }
      }
   }
}
